package com.wxpay.config;

import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.text.DateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Properties;

/**
 * MyBatis SQL执行日志拦截器
 * 只记录异常SQL和执行时间较长的SQL
 */
@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class})
})
@Component
public class SqlLogInterceptor implements Interceptor {

    private static final Logger logger = LoggerFactory.getLogger(SqlLogInterceptor.class);
    
    // SQL执行时间阈值，超过这个时间才记录日志（毫秒）
    private static final long SLOW_SQL_THRESHOLD = 200;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        long startTime = System.currentTimeMillis();
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        Object parameter = invocation.getArgs().length > 1 ? invocation.getArgs()[1] : null;
        String sqlId = mappedStatement.getId();
        BoundSql boundSql = mappedStatement.getBoundSql(parameter);
        Configuration configuration = mappedStatement.getConfiguration();
        String sql = getSql(configuration, boundSql, sqlId);
        String methodName = invocation.getMethod().getName();
        
        // 记录SQL语句(更新操作总是记录)
        boolean isUpdate = "update".equals(methodName);
        if (isUpdate) {
            logger.info("执行SQL(update) - SQL: {}", sql);
        }
        
        Object result;
        try {
            // 执行SQL
            result = invocation.proceed();
        } catch (Exception e) {
            // 记录SQL执行异常
            logger.error("SQL执行异常 - SQL: {}, 执行耗时: {}ms, 异常: {}", 
                    sql, System.currentTimeMillis() - startTime, e.getMessage());
            throw e;
        }
        
        // 计算执行时间
        long endTime = System.currentTimeMillis();
        long time = endTime - startTime;
        
        // 记录SQL执行结果
        if (isUpdate) {
            // 更新类操作记录影响行数
            int affectedRows = (int) result;
            logger.info("SQL执行结果(update) - 影响行数: {}, 执行耗时: {}ms", affectedRows, time);
        } else {
            // 查询类操作只记录慢SQL
            if (time > SLOW_SQL_THRESHOLD) {
                List<?> resultList = (List<?>) result;
                int resultSize = resultList != null ? resultList.size() : 0;
                logger.warn("SQL执行较慢(query) - 返回结果数: {}, 执行耗时: {}ms, SQL: {}", 
                        resultSize, time, sql);
            }
        }
        
        return result;
    }

    /**
     * 获取完整SQL语句及参数
     */
    private String getSql(Configuration configuration, BoundSql boundSql, String sqlId) {
        String sql = boundSql.getSql().replaceAll("\\s+", " ");
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        Object parameterObject = boundSql.getParameterObject();
        
        // 没有参数，直接返回SQL
        if (parameterMappings == null || parameterMappings.isEmpty()) {
            return String.format("SQL: %s", sql);
        }
        
        try {
            TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
            
            // 替换SQL中的?为实际参数值
            StringBuilder sqlWithParams = new StringBuilder();
            sqlWithParams.append("SQL: ").append(sql);
            sqlWithParams.append("\n参数: {");
            
            for (ParameterMapping parameterMapping : parameterMappings) {
                // 简化参数模式判断，直接获取所有参数
                String propertyName = parameterMapping.getProperty();
                Object value;
                if (boundSql.hasAdditionalParameter(propertyName)) {
                    value = boundSql.getAdditionalParameter(propertyName);
                } else if (parameterObject == null) {
                    value = null;
                } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                    value = parameterObject;
                } else {
                    MetaObject metaObject = configuration.newMetaObject(parameterObject);
                    value = metaObject.getValue(propertyName);
                }
                
                sqlWithParams.append("\n  ").append(propertyName).append(" = ");
                
                if (value instanceof String) {
                    sqlWithParams.append("'").append(value).append("'");
                } else if (value instanceof Date) {
                    DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);
                    sqlWithParams.append("'").append(dateFormat.format(value)).append("'");
                } else {
                    sqlWithParams.append(value);
                }
                
                sqlWithParams.append(",");
            }
            
            if (sqlWithParams.charAt(sqlWithParams.length() - 1) == ',') {
                sqlWithParams.deleteCharAt(sqlWithParams.length() - 1);
            }
            
            sqlWithParams.append("\n}");
            
            return sqlWithParams.toString();
            
        } catch (Exception e) {
            logger.warn("解析SQL参数异常: {}", e.getMessage());
            return String.format("SQL: %s", sql);
        }
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        // 可以在这里配置插件的特性
    }
} 